package __回溯算法

import "sort"

/*
https://leetcode.cn/problems/combination-sum-ii/

40. 组合总和 II
给定一个候选人编号的集合 candidates 和一个目标数 target ，
找出 candidates 中所有可以使数字和为 target 的组合。

candidates 中的每个数字在每个组合中只能使用 【一次】 。 给定的数组中是【有重复元素】的.
注意：解集不能包含重复的组合。

示例 1:
输入: candidates = [10,1,2,7,6,1,5], target = 8,
输出:
[

	[1,1,6],
	[1,2,5],
	[1,7],
	[2,6]

]

示例 2:
输入: candidates = [2,5,2,1,2], target = 5,
输出:
[

	[1,2,2],
	[5]
*/
var (
	res40  [][]int
	path40 []int
	used   []bool
)

func combinationSum2(candidates []int, target int) [][]int {
	res40, path40 = make([][]int, 0), make([]int, 0, len(candidates)) //初始化数组
	used = make([]bool, len(candidates))                              //初始化使用标记数组
	sort.Ints(candidates)                                             // 排序，为剪枝做准备
	dfs40(candidates, 0, target)                                      //进行回溯

	return res40
}

func dfs40(candidates []int, start int, target int) {
	if target == 0 { // target 不断减小，如果为0说明达到了目标值
		temp := make([]int, len(path40))
		copy(temp, path40)
		res40 = append(res40, temp)
		return
	}

	for i := start; i < len(candidates); i++ { //遍历参数数组元素
		if candidates[i] > target { //如果当前元素直接比目标元素都大 剪枝，提前返回
			break
		}

		// used[i - 1] == true，说明同一树枝candidates[i - 1]使用过
		// used[i - 1] == false，说明同一树层candidates[i - 1]使用过
		if i > 0 && candidates[i] == candidates[i-1] && used[i-1] == false { //如果i>0 && 相邻两个元素相等 && 前一位相等元素并没有被使用过, 说明是同一层的处理情况, 因为前一位和当前元素相等, 所有的情况已经都被处理过了, 所以当前元素需要跳过
			continue //如果used[i-1]=false, 那么相邻的两个元素是同一层的, 所以需要跳过  如果used[i-1]=true, 那么是同一枝的, 需要正常使用组成结果
		}

		path40 = append(path40, candidates[i])       //路径中添加当前元素
		used[i] = true                               //当前元素标记为使用过
		dfs40(candidates, i+1, target-candidates[i]) //调用递归
		used[i] = false                              //将当前标记为没有使用过
		path40 = path40[:len(path40)-1]              //剔除path数组末尾元素
	}
}
